#include "ddnswgets.h"

#ifndef _WIN32
#define DWERR_SIZE      25

const char *err_str[DWERR_SIZE + 1] = {
	    "Success",
		"Connection Failed With Server : Check Query Url",
		"Continue Download Failed",
		"Opening a File(fstat) at Client Failed",
		"Writing to Network Stream Failed",
		"Reading from Network Stream Failed",
		"Error Opening FTP Session",
		"File Not exits at Server",
		"Login Failed At Server",
		"Error in GetFile size at Server",
		"Entering PassiveMode Failed at Server",
		"Error In Url String of Client",
		"File/Path Not Exists at Server : Check Url,File",
		"Fgets Failed at Server",
		"Name Resolution Failed at Server",
		"Http : No Response from Http Server",
		"Http : Content Length Error",
		"Http : Transfer Encode Error",
		"Http : ManyRedirections Error",
		"Http : BadHeader Error",
		"Http : Connection TimeOut",
		"Http : File Not Found",
		"Http : Unknown Request",
		"Http : Unknown Error",
		"No Input URL Given To Wget",
		"No Result File Speicfied for Wget"
					
};
#define close_delete_and_die(s) {\
	if (output != stdout) { \
		fclose(output); \
		output = NULL;	\
        unlink(_CDISK_REAULT_FILE); \
	} \
	bb_errno = s; \
	return FAILURE; }
	
struct host_info {
	char *host;
	int port;
	char *path;
	int is_ftp;
	char *user;
};


static int bb_lookup_host(struct sockaddr_in *s_in, const char *host);
static unsigned short bb_lookup_port(const char *port, const char *protocol, unsigned short default_port);
static int safe_strtoul(char *arg, unsigned long* value);

static int parse_url(char *url, struct host_info *h);
static FILE *open_socket(struct sockaddr_in *s_in);
static char *gethdr(char *buf, size_t bufsiz, FILE *fp, int *istrunc);
static int ftpcmd(char *s1, char *s2, FILE *fp, char *buf);

//Added by Srini Only
int wget(char*);
int  procWgetError(int);

/* Globals (can be accessed from signal handlers */
static off_t filesize = 0;		/* content-length of the file */
static int chunked = 0;			/* chunked transfer encoding */
int bb_errno;                   /* Error Number Global One */

#define BUFFLEN 256
#define MSGLEN 80
#define ERR_CODE_SIZE 9
#define _CDISK_REAULT_FILE "ddns.result"

const char *err_Code[ERR_CODE_SIZE+1] ={
    "NS_UPDATE_PASS",
    "NS_DELETE_PASS",
    "NS_REGISTER_PASS",
    "NS_HOST_AVAIL",
    "REG_FAIL",
    "NS_NAMEXIST",
    "NS_COMM_FAIL",
    "NS_AUTH_MISMATCH",
    "DB_NO_DATA",
    "GEN_FAIL"
};

int stringTrim(char *str)
{
    char tempStr[BUFFLEN];
    int i, j, count;

    i=j=count=0;
    strcpy(tempStr, "");
    while((str[i] == ' ') || (str[i] == '\t')) i++;

    while((str[i]!='\0') && (str[i] != '\n'))
    {
        if(str[i] == ' ')
            count++;
        else
        {
            if(count >= 1)
            {
                tempStr[j++] = ' ';
                count = 0;
            }
            tempStr[j++] = str[i];
        }
        i++;
    }
    tempStr[j] = '\0';
    strcpy(str+0, tempStr);
    return SUCCESS;
}

int readResultFile(char *mesg)
{
    FILE *fptr;
    fptr = fopen(_CDISK_REAULT_FILE, "r");
    if(fptr == NULL)
    {
        return FAILURE;
    }

    while(!feof(fptr))
    {
        fgets(mesg, MSGLEN, fptr);
    }

    fclose(fptr);
    stringTrim(mesg);

    return SUCCESS;
}

char respMsg[MSGLEN];

char* clientProcess()
{
    int dwRetVal, i;
    char cliMsg[BUFFLEN];

    memset(respMsg, 0, MSGLEN);
    memset(cliMsg, 0, BUFFLEN);

    if((dwRetVal = readResultFile(respMsg)) == FAILURE)
    {
        return "Not found result file";
    }

    for(i = 0; i < ERR_CODE_SIZE+1; i++)
    {
        if(!strcmp(respMsg, err_Code[i]))
        {
            return err_Code[i];
        }
    }
    return respMsg;
}

int                                                                                                               
procWgetError(int dwError)                                                                                        
{                                                                                                                 
	if (dwError >= 0 && dwError <= DWERR_SIZE){                                                                   
		//printf("Wget Error : %s\n", err_str[dwError]);   
		//return FAIL;
	}		
	//else	                                                                                                                  
		//printf("Wget Unknown error (%d)\n", dwError);                                                                 
	
	    return SUCCESS;   
	//return FAIL;                                                                                            
} 

static int
connectWithTimeout(int fd, struct sockaddr *remote, int len, int secs, int *err)
{
	int				saveflags, ret, back_err; 
	fd_set			fd_w; 
	struct timeval	timeout; 

	// mang 
	// struct sockaddr_in sa_loc;

	//Kim YoungMan Own Function for Connection Time-Outs
	if ((saveflags = fcntl(fd, F_GETFL, 0)) < 0) {
		*err = errno; 
		return -1; 
	} 

	// mang // bind local ip address
	//memset(&sa_loc, 0, sizeof(struct sockaddr_in));
	//sa_loc.sin_family = AF_INET;
	//sa_loc.sin_port = htons(80);
	//sa_loc.sin_addr.s_addr = inet_addr("1.234.27.191"); // test local ip address
	//bind(fd, (struct sockaddr *)&sa_loc, sizeof(struct sockaddr));

	/* Set non blocking */ 
	if (fcntl(fd, F_SETFL, saveflags | O_NONBLOCK) < 0) { 
		*err = errno; 
		return -1; 
	} 

	/* This will return immediately */ 
	*err = connect(fd, remote, len); 
	back_err = errno; 

	/* restore flags */ 
	if (fcntl(fd, F_SETFL, saveflags) < 0) { 
		*err = errno; 
		return -1; 
	} 

	/* return unless the connection was successful or the connect is 
	 * still in progress. */ 
	if (*err < 0 && back_err != EINPROGRESS) { 
		*err = errno; 
		return -1; 
	} 

	timeout.tv_sec = (long)secs; 
	timeout.tv_usec = 0L; 

	FD_ZERO(&fd_w); 
	FD_SET(fd, &fd_w); 

	*err = select(FD_SETSIZE, NULL, &fd_w, NULL, &timeout); 
	if(*err<0) { 
		*err = errno; 
		return -1; 
	} 

	/* 0 means it timeout out & no fds changed */ 
	if(*err == 0) { 
		*err = ETIMEDOUT; 
		return -1; 
	} 

	/* Get the return code from the connect */ 
	len = sizeof(ret); 
	*err = getsockopt(fd, SOL_SOCKET, SO_ERROR, &ret, &len); 
	if (*err < 0) { 
		*err = errno; 
		return -1; 
	} 

	/* ret=0 means success, otherwise it contains the errno */ 
	if (ret) { 
		*err = ret; 
		return -1; 
	} 

	*err = 0; 
	return 0; 
}


/* Return network byte ordered port number for a service.
 * If "port" is a number use it as the port.
 * If "port" is a name it is looked up in /etc/services, if it isnt found return
 * default_port
 */
unsigned short bb_lookup_port(const char *port, const char *protocol, unsigned short default_port)
{
	unsigned short port_nr = htons(default_port);
	if (port) {
		char *endptr;
		int old_errno;
		long port_long;

		/* Since this is a lib function, we're not allowed to reset errno to 0.
		 * Doing so could break an app that is deferring checking of errno. */
		old_errno = errno;
		errno = 0;
		port_long = strtol(port, &endptr, 10);
		if (errno != 0 || *endptr!='\0' || endptr==port || port_long < 0 || port_long > 65535) {
			struct servent *tserv = getservbyname(port, protocol);
			if (tserv) {
				port_nr = tserv->s_port;
			}
		} else {
			port_nr = htons(port_long);
		}
		errno = old_errno;
	}
	return port_nr;
}

int safe_strtoul(char *arg, unsigned long* value)
{
	char *endptr;

	*value = strtoul(arg, &endptr, 0);
	if (*endptr!='\0' || endptr==arg) {
		return 1;
	}
	return 0;
}


int bb_lookup_host(struct sockaddr_in *s_in, const char *host)
{
	struct hostent *he;

	memset(s_in, 0, sizeof(struct sockaddr_in));
	s_in->sin_family = AF_INET;
	if ( (he = gethostbyname(host)) == NULL ) {
		bb_errno = ERR_NAMERES;
		return FAILURE;
	}
	memcpy(&(s_in->sin_addr), he->h_addr_list[0], he->h_length);

	return SUCCESS;
}


/* Read NMEMB elements of SIZE bytes into PTR from STREAM.  Returns the
 * number of elements read, and a short count if an eof or non-interrupt
 * error is encountered.  */
static size_t safe_fread(void *ptr, size_t size, size_t nmemb, FILE *stream)
{
	size_t ret = 0;

	do {
		clearerr(stream);
		ret += fread((char *)ptr + (ret * size), size, nmemb - ret, stream);
	} while (ret < nmemb && ferror(stream) && errno == EINTR);

	return ret;
}

/* Write NMEMB elements of SIZE bytes from PTR to STREAM.  Returns the
 * number of elements written, and a short count if an eof or non-interrupt
 * error is encountered.  */
static size_t safe_fwrite(void *ptr, size_t size, size_t nmemb, FILE *stream)
{
	size_t ret = 0;

	do {
		clearerr(stream);
		ret += fwrite((char *)ptr + (ret * size), size, nmemb - ret, stream);
	} while (ret < nmemb && ferror(stream) && errno == EINTR);

	return ret;
}

/* Read a line or SIZE - 1 bytes into S, whichever is less, from STREAM.
 * Returns S, or NULL if an eof or non-interrupt error is encountered.  */
static char *safe_fgets(char *s, int size, FILE *stream)
{
	char *ret;

	do {
		clearerr(stream);
		ret = fgets(s, size, stream);
	} while (ret == NULL && ferror(stream) && errno == EINTR);

	return ret;
}


//Modified Wget Function which Works for Http and ftp Requests Here
int wget(char *url)
{
	int n,status,port,errCode,try=5;
	unsigned long opt;
	
	char *s, buf[512];
	struct stat sbuf;
	struct host_info server, target;
	struct sockaddr_in s_in;

	
	FILE *sfp = NULL;			/* socket to web/ftp server			*/
	FILE *dfp = NULL;			/* socket to ftp server (data)		*/
	long beg_range = 0L;		/*   range at which continue begins	*/
	int got_clen = 0;			/* got content-length: from server	*/
	FILE *output;				/* socket to web server				*/

	//Check Url is empty here
	if((strcmp(url,"") == 0)||(strlen(url)<4)){
		bb_errno = ERR_NOINPUT_URL;
		return FAILURE;
	}
	
	//Now Parse The Input Url Here and Determine Http or Ftp Request
	if(parse_url(url,&target) == FAILURE)
	{
		printf("Error In Url Parsing\n");
		bb_errno = ERR_URL;
		return FAILURE;
	}
	
	server.host = target.host;
	server.port = target.port;

	/*
	 * Open the output file stream.
	 */
    output = fopen(_CDISK_REAULT_FILE,"w+");
		
	if(output == NULL)
		close_delete_and_die(ERR_FOPEN);
		
	/* We want to do exactly _one_ DNS lookup
	 * and we want to connect to only one IP... 
	 */
	if(bb_lookup_host(&s_in, server.host) == FAILURE){
		fclose(output);
		//Here Later I had to Return Proper Error
		return FAILURE;
	}
	s_in.sin_port = server.port;

	if (!target.is_ftp) 
	{ /* *HTTP session */
		do {
			got_clen = chunked = 0;
			
			if (! --try)
				close_delete_and_die(ERR_HMANY_REDIR);
			
			/*Open socket to http server*/
			if (sfp) fclose(sfp);
			sfp = open_socket(&s_in);

		        //YoungMan not ckecking Return Code of Open_socket function 
			//if sfp is null return from here because this will result in Segmentation fault later
			
			if(sfp == NULL)
				return FAILURE;
			
			/*Send HTTP request.*/
			fprintf(sfp, "GET /%s HTTP/1.1\r\n", target.path);
			fprintf(sfp, "Host: %s\r\nUser-Agent: Wget\r\n", target.host);
			fprintf(sfp,"Connection: close\r\n\r\n");
			
		 	/* Retrieve HTTP response line and check for "200" status code.*/
			
			read_response:
			
			if (fgets(buf, sizeof(buf), sfp) == NULL){
				bb_errno = ERR_HSERVER_NORESP;
				return FAILURE;
			}
					
			for (s = buf ; *s != '\0' && !isspace(*s) ; ++s) ;
			for ( ; isspace(*s) ; ++s) ;

			switch (status = atoi(s)) {
				case 0:
				case 100:
					while (gethdr(buf, sizeof(buf), sfp, &n) != NULL);
					goto read_response;
				case 200:
					if (output != stdout)
                        output = freopen(_CDISK_REAULT_FILE, "w", output);
					break;
				case 300:	/* redirection */
				case 301:
				case 302:
				case 303:
					break;
				case 206:
					
				default:
					errCode = atoi(s);
					switch(errCode)
						{
							case 401 :
								bb_errno = ERR_HCONTIME_OUT;
								break;
							case 404 :
								bb_errno = ERR_HFILENOT_FOUND;
								break;
							case 408 :
								bb_errno = ERR_HUNKNOWN_REQ;
								break;
							default :
								bb_errno = ERR_HUNKNOWN;
					    }		
				return FAILURE;	
							
			}

			/*
			 * Retrieve HTTP headers.
			 */
			while ((s = gethdr(buf, sizeof(buf), sfp, &n)) != NULL) {
				if (strcasecmp(buf, "content-length") == 0) {
					unsigned long value;
					if (safe_strtoul(s, &value)) 
						close_delete_and_die(ERR_HCONTENT_LEN);
					
					filesize = value;
					got_clen = 1;
					continue;
				}
				if (strcasecmp(buf, "transfer-encoding") == 0) {
					if (strcasecmp(s, "chunked") == 0) {
						chunked = got_clen = 1;
					}
				   	else 
				    	close_delete_and_die(ERR_HTRANSFER_ENCODE);
					
				}
				if (strcasecmp(buf, "location") == 0) {
					if (s[0] == '/')
						target.path = strdup(s+1);
					else {
						parse_url(strdup(s), &target);
						bb_lookup_host(&s_in, server.host);
						s_in.sin_port = server.port;
						break;
					}
				}
			}
		} while(status >= 300);
		
		dfp = sfp; 
	}   //End of the Http Request Processing here
	
	else
	{

	/*
	 *  FTP session
	 */
	if (! target.user)
		target.user = strdup("anonymous:busybox@");

	sfp = open_socket(&s_in);
	if (!sfp) {
		fclose(output);
		return FAILURE;
	}
	if (ftpcmd(NULL, NULL, sfp, buf) != 220) {
		fclose(sfp);
		close_delete_and_die(ERR_CONN);
	}

	/*
	 * Splitting username:password pair,
	 * trying to log in
	 */
	s = strchr(target.user, ':');
	if (s)
		*(s++) = '\0';
	switch(ftpcmd("USER ", target.user, sfp, buf)) {
		case 230:
			break;
		case 331:
			if (ftpcmd("PASS ", s, sfp, buf) == 230)
				break;
			/* FALLTHRU (failed login) */
		default:
			fclose(sfp);
			close_delete_and_die(ERR_LOGFAILED);
	}

	ftpcmd("CDUP", NULL, sfp, buf);
	ftpcmd("TYPE I", NULL, sfp, buf);

	/*
	 * Querying file size
	 */
	if (ftpcmd("SIZE /", target.path, sfp, buf) == 213) {
		unsigned long value;
		if (safe_strtoul(buf+4, &value)) {
			fclose(sfp);
			close_delete_and_die(ERR_FSIZE);
		}
		filesize = value;
		got_clen = 1;
	}

	/*
	 * Entering passive mode
	 */
	if (ftpcmd("PASV", NULL, sfp, buf) !=  227) {
		fclose(sfp);
		close_delete_and_die(ERR_PASV);
	}
	s = strrchr(buf, ',');
	*s = 0;
	port = atoi(s+1);
	s = strrchr(buf, ',');
	port += atoi(s+1) * 256;
	s_in.sin_port = htons(port);
	dfp = open_socket(&s_in);
	if (!dfp) {
		fclose(sfp);
		close_delete_and_die(ERR_CONN);
	}

	if (ftpcmd("RETR /", target.path, sfp, buf) > 150) {
		fclose(sfp);
		fclose(dfp);
		close_delete_and_die(ERR_FILE_UNAVAIL);
	}

  }//End of Ftp Session
	
	/*
	**Now Retrieve the file ftp or http
	*/
	if (chunked) {
		fgets(buf, sizeof(buf), dfp);
		filesize = strtol(buf, (char **) NULL, 16);
	}

	do {
		while ((filesize > 0 || !got_clen) && (n = safe_fread(buf, 1, ((chunked || got_clen) && \
				(filesize < sizeof(buf)) ? filesize : sizeof(buf)), dfp)) > 0)
		{
			if (safe_fwrite(buf, 1, n, output) != n) {
				bb_errno = ERR_WRITE;
				fclose(sfp);
				fclose(dfp);
				close_delete_and_die(ERR_WRITE);
			}
			if (got_clen) {
				filesize -= n;
			}
		}

		if (chunked) {
			safe_fgets(buf, sizeof(buf), dfp); /* This is a newline */
			safe_fgets(buf, sizeof(buf), dfp);
			filesize = strtol(buf, (char **) NULL, 16);
			if (filesize==0) {
				chunked = 0; /* all done! */
			}
		}

		if (n == 0 && ferror(dfp)) {
			fclose(sfp);
			fclose(dfp);
			close_delete_and_die(ERR_NETREAD);
		}
	} while (chunked);

	
	//First Close  File which was written by ftp or http server
	fclose(output);

	if (target.is_ftp) {
		if (ftpcmd(NULL, NULL, sfp, buf) != 226) {
			fclose(sfp);
			close_delete_and_die(ERR_CONN);
		}
		ftpcmd("QUIT", NULL, sfp, buf);
		fclose(sfp);
	}
	
	else
  		fclose(dfp);
	
	return SUCCESS;
}

static int parse_url(char *url, struct host_info *h)
{
	char *cp, *sp, *up, *pp;

	if (strncmp(url, "http://", 7) == 0) {
		h->port = bb_lookup_port("http", "tcp", 80);
		h->host = url + 7;
		h->is_ftp = 0;
	} else if (strncmp(url, "ftp://", 6) == 0) {
		h->port = bb_lookup_port("ftp", "tfp", 21);
		h->host = url + 6;
		h->is_ftp = 1;
	} else{
		bb_errno = ERR_URL;
		return FAILURE;
	}

	sp = strchr(h->host, '/');
	if (sp) {
		*sp++ = '\0';
		h->path = sp;
	} else
		h->path = strdup("");

	up = strrchr(h->host, '@');
	if (up != NULL) {
		h->user = h->host;
		*up++ = '\0';
		h->host = up;
	} else
		h->user = NULL;

	pp = h->host;
	
	cp = strchr(pp, ':');
	if (cp != NULL) {
		*cp++ = '\0';
		h->port = htons(atoi(cp));
	}
	return SUCCESS;
}


FILE *open_socket(struct sockaddr_in *s_in)
{

	FILE	*fp;
	int		ret;

	int s = socket(AF_INET, SOCK_STREAM, 0);
	//connection TimeOut Changed to 2 instead of 1 due to some problem with Http Response 
	if(connectWithTimeout(s, (struct sockaddr *)s_in, sizeof(struct sockaddr_in), 1, &ret) < 0)
	{
		close(s);
		bb_errno = ERR_CONNECT;
		return NULL;
	}

#if 1L
        do {
            struct linger s_linger;
            (void)memset((void *)(&s_linger), 0, sizeof(s_linger));
            s_linger.l_onoff = 1;
            s_linger.l_linger = 0;
            (void)setsockopt(s, SOL_SOCKET, SO_LINGER, &s_linger, (socklen_t)sizeof(s_linger));
        }while(0);
#endif	

	fp = fdopen(s, "r+");
	if (fp == NULL) {
		bb_errno = ERR_FOPEN;
		return NULL;
	}

	return fp;
}


char *gethdr(char *buf, size_t bufsiz, FILE *fp, int *istrunc)
{
	char *s, *hdrval;
	int c;

	*istrunc = 0;

	/* retrieve header line */
	if (fgets(buf, bufsiz, fp) == NULL)
		return NULL;

	/* see if we are at the end of the headers */
	for (s = buf ; *s == '\r' ; ++s)
		;
	if (s[0] == '\n')
		return NULL;

	/* convert the header name to lower case */
	for (s = buf ; isalnum(*s) || *s == '-' ; ++s)
		*s = tolower(*s);

	/* verify we are at the end of the header name */
	if (*s != ':'){
		bb_errno = ERR_HBAD_HEADER;
		return NULL;
		//close_delete_and_die("bad header line: %s", buf);
		}

	/* locate the start of the header value */
	for (*s++ = '\0' ; *s == ' ' || *s == '\t' ; ++s)
		;
	hdrval = s;

	/* locate the end of header */
	while (*s != '\0' && *s != '\r' && *s != '\n')
		++s;

	/* end of header found */
	if (*s != '\0') {
		*s = '\0';
		return hdrval;
	}

	/* Rats!  The buffer isn't big enough to hold the entire header value. */
	while (c = getc(fp), c != EOF && c != '\n')
		;
	*istrunc = 1;
	return hdrval;
}

static int ftpcmd(char *s1, char *s2, FILE *fp, char *buf)
{
	if (s1) {
		if (!s2) s2="";
		fprintf(fp, "%s%s\r\n", s1, s2);
		fflush(fp);
	}

	do {
		char *buf_ptr;

		if (fgets(buf, 510, fp) == NULL) {
			bb_errno = ERR_FGET;
			return FAILURE;
		}
		buf_ptr = strstr(buf, "\r\n");
		if (buf_ptr) {
			*buf_ptr = '\0';
		}
	} while (! isdigit(buf[0]) || buf[3] != ' ');

	return atoi(buf);
}
#endif

